#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;
#define COMDSIZE 1024
#define envsize 1024
#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
string filename;
int falg = 0;
char pwd[1024];
char tem[1024];
char *g_env[envsize];
int g_envs = 0;
int lastexitcode = 0;
char *g_argc[1024];
int g_argcs = 0;
void Initenv()
{
    memset(g_env, 0, sizeof(g_env));
    extern char **environ;
    for (int i = 0; environ[i]; i++)
    {
        g_env[g_envs] = (char *)malloc(strlen(environ[i])+1);
        strcpy(g_env[g_envs], environ[i]);
        g_envs++;
    }
    // this is my envc
//    g_env[g_envs++] = (char *)"test=10000";
    g_env[g_envs] = NULL;
    for (int i = 0; g_env[i]; i++)
    {
        putenv(g_env[i]);
    }
    environ = g_env;
}

const char *GetHOME()
{
    return getenv("LOGNAME");
}
const char *GetHOSTNAME()
{
    const char *ret = getenv("HOSTNAME");
    return ret;
}

const char *Getpwd()
{
    const char *ret = getcwd(pwd, sizeof(pwd));
    if (ret)
    {
        snprintf(tem, sizeof(tem), "PWD=%s", pwd);
        putenv(tem);
    }
    return ret;
}
const char * DirName(const char* pwd)
{
#define SLASH "/"
    std::string dir = pwd;
    if (dir == SLASH) return SLASH;
    auto pos = dir.rfind(SLASH);
    if (pos == std::string::npos) return "BUG?";
    return dir.substr(pos + 1).c_str();
}
void GetCommand(char *buffer, int size)
{
#define format "[%s@%s %s]# "
 snprintf(buffer, size, format, GetHOME(), GetHOSTNAME(),DirName(Getpwd()));
}
void PrintCommandPrompt()
{
    char buffer[COMDSIZE];
    GetCommand(buffer, sizeof(buffer));
    printf("%s", buffer);
    fflush(stdout);
}
bool GetCommandLine(char buffer[], int size)
{
    char *ret = fgets(buffer, size, stdin);
    int index=strlen(buffer)-1;
    buffer[index]=0;
    int lengrh=strlen(buffer);
    if(lengrh<=0) return false;
    else return true;

}

int SkipSpace(char arr[], int cur,int n)
{
    while (cur <= n && isspace(arr[cur]))
        cur++;
    if (cur > n)
        return -1;
    else
        return cur;
}
void RedirCheck(char comd[])
{

    int begin = 0;
    int end = strlen(comd) - 1;
    int len=end;
    if(!filename.empty())
    filename.clear();
    falg = NONE_REDIR;
    // ls -a -l >
    while (begin < end)
    {
        char tem = comd[end];
        if (tem == '<')
        {
            comd[end]=0;
            falg = INPUT_REDIR;
            int index = SkipSpace(comd, end + 1,len);
            if (index == -1)
                filename = "";
            else
                filename = comd + index;
        }
        else if (tem == '>')
        {
            if (comd[end - 1] == '>')
            {
                comd[end-1]=0;
                falg = APPEND_REDIR;
                int index = SkipSpace(comd, end + 1,len);
                if (index == -1)
                    filename = "";
                else
                    filename = comd + index;
            }
            else
            {
                comd[end]=0;
                falg = OUTPUT_REDIR;
                int index = SkipSpace(comd, end + 1,len);
                if (index == -1)
                    filename = "";
                else
                    filename = comd + index;
            }
        }
        else
        {
            end--;
        }
    }
    if(filename=="") filename="bug?";
}

bool CommandParse(char cmd[])
{
    memset(g_argc, 0, sizeof(g_argc));
    g_argcs = 0;
    char *ret = NULL;
    const char *ep = (const char *)" ";

    for (ret = strtok(cmd, ep); ret != NULL; ret = strtok(NULL, ep))
    {
        g_argc[g_argcs++] = ret;
    }
    g_argc[g_argcs] = NULL;
    return g_argcs == 0 ? false : true;
}

void Cd()
{
    string where;
    if (g_argcs == 1)
    {
        where = pwd;
    }
    else
    {
        where = g_argc[1];
        if (where == string("~"))
        {
            where = "/home/";
            where += GetHOME();
            where += "/";
        }
        else if (where == string(".."))
        {
        }
        else if (where == string("."))
        {
        }
        else if (where == string("-"))
        {
            where = "/home/";
            where += GetHOME();
            where += "/";
        }
    }
    chdir(where.c_str());
}
void Pwd()
{
    printf("%s\n",pwd);
}
void ECHO()
{
}
bool CheckAndExecBuiltin()
{
    char *tem = g_argc[0];
    if (strcmp(tem, "cd") == 0)
    {
        Cd();
        return true;
    }
    else if (strcmp(tem, "pwd") == 0)
    {
        Pwd();
        return true;
    }
    else if (strcmp(tem, "echo") == 0)
    {
        ECHO();
        return true;
    }
    else
    {
        return false;
    }
}
void Execute()
{
    int id = fork();
    if (id == 0)
    {
        if (falg != NONE_REDIR)
        {
            if (falg == INPUT_REDIR)
            {
                int fd = open(filename.c_str(), O_RDONLY);
                if (fd < 0)
                    exit(1);
                dup2(fd, 0);
                close(fd);
            }
            else if (falg == OUTPUT_REDIR)
            {
                umask(0);
                int fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
                if (fd < 0)
                    exit(1);
                dup2(fd, 1);
                close(fd);
            }
            else
            {
                umask(0);
                int fd = open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
                if (fd < 0)
                    exit(1);
                dup2(fd, 1);
                close(fd);
            }
        }
        execvp(g_argc[0], g_argc);
        exit(1);
    }
    // parent
    int status = 0;
    pid_t ans1 = waitpid(id, &status, 0);
    if (ans1 == id)
    {
        lastexitcode = WEXITSTATUS(status);
    }
}
int main()
{
    Initenv();
 while (1)
 {
     // 1.
     PrintCommandPrompt();
     // 2.GetCommandLine
     char buffer[COMDSIZE];
       if (!GetCommandLine(buffer, sizeof(buffer)))
         continue;
     // 3. RedirCheck(commandline);
     RedirCheck(buffer);
     // 4.
     if (!CommandParse(buffer))
         continue;
     // 5.
     if (CheckAndExecBuiltin())
         continue;

     // 6.
     Execute();
 }
    return 0;
}
